package com.kj.tms.common.utils;
import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.StringTokenizer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @title  台帐表内计算
 * @author ddhuang +,-,*,/四则运算的表达式逆波兰解析计算类,精确计算,应用BigDecimal类处理
 *         支持负数,但规范除整个表达式第一个数为负数时可以不出现在'('后，其它表达式中间任何位置的
 *         负数必须出现在'('后,即：用括号括起来。比如：
 *         -3+(-2+1)*10或-3+((-2)+1)*10或(-3)+(-2+1)*10或(-3)+((-2)+1)*10
 */
public class MathExpress {
	/**
	 * +
	 */
	private final static String OP1 = "+";

	/**
	 * -
	 */
	private final static String OP2 = "-";

	/**
	 * *
	 */
	private final static String OP3 = "*";

	/**
	 * /
	 */
	private final static String OP4 = "/";

	/**
	 * ^
	 */
	// private final static String OP5 = "^";

	/**
	 * %
	 */
	// private final static String OP6 = "%";
	/**
	 * (
	 */
	private final static String OPSTART = "(";

	/**
	 * )
	 */
	private final static String OPEND = ")";

	/**
	 * !用来替代负数前面的'-'
	 */
	// private final static String NEGATIVESING = "!";

	/**
	 * !用来替代负数前面的'+'
	 */
	// private final static String PLUSSING = "@";

	/**
	 * '#'用来代表运算级别最低的特殊字符
	 */
	// private final static String LOWESTSING = "#";

	// 最原始的四则运算式
	private String expBase;

	// 经过初始化处理后的四则运算式
	private String expInited;

	// 精度
	private int precision = 10;

	// 取舍模式
	private RoundingMode roundingMode = RoundingMode.HALF_UP;

	// 精度上下文
	private MathContext mc;

	// 四则运算解析
	private List<String> expList = new ArrayList<String>();

	// 存放逆波兰表达式
	private List<String> rpnList = new ArrayList<String>();

	public MathExpress() {
	}

	public MathExpress(String expBase) {
		init(expBase, this.precision, this.roundingMode);
	}

	public MathExpress(String expBase, int precision, RoundingMode roundingMode) {
		init(expBase, precision, roundingMode);
	}

	public void init(String expBase, int precision, RoundingMode roundingMode) {
		expBase = expBase.trim();
		expBase = expBase.toUpperCase();
		expBase = parseExpression(expBase);

		this.expBase = expBase;
		this.precision = precision;
		this.roundingMode = roundingMode;
		this.mc = new MathContext(precision, roundingMode);
		this.expInited = initExpress(expBase);

		StringTokenizer st = new StringTokenizer(this.expInited, "+-*/^%()",
				true);
		while (st.hasMoreElements()) {
			this.expList.add(st.nextElement().toString().trim());
		}

		this.rpnList = initRPN(this.expList);
	}

	/**
	 * @return the expBase
	 */
	public String getExpBase() {
		return expBase;
	}

	/**
	 * @param expBase
	 *            the expBase to set
	 */
	public void setExpBase(String expBase) {
		this.expBase = expBase;
	}

	/**
	 * @return the expInited
	 */
	public String getExpInited() {
		return expInited;
	}

	/**
	 * @param expInited
	 *            the expInited to set
	 */
	public void setExpInited(String expInited) {
		this.expInited = expInited;
	}

	/**
	 * @return the precision
	 */
	public int getPrecision() {
		return precision;
	}

	/**
	 * @param precision
	 *            the precision to set
	 */
	public void setPrecision(int precision) {
		this.precision = precision;
	}

	/**
	 * @return the roundingMode
	 */
	public RoundingMode getRoundingMode() {
		return roundingMode;
	}

	/**
	 * @param roundingMode
	 *            the roundingMode to set
	 */
	public void setRoundingMode(RoundingMode roundingMode) {
		this.roundingMode = roundingMode;
	}

	/**
	 * @return the expList
	 */
	public List<String> getExpList() {
		return expList;
	}

	/**
	 * @param expList
	 *            the expList to set
	 */
	public void setExpList(List<String> expList) {
		this.expList = expList;
	}

	/**
	 * @return the rpnList
	 */
	public List<String> getRpnList() {
		return rpnList;
	}

	/**
	 * @param rpnList
	 *            the rpnList to set
	 */
	public void setRpnList(List<String> rpnList) {
		this.rpnList = rpnList;
	}

	/**
	 * @return the mc
	 */
	public MathContext getMc() {
		return mc;
	}

	/**
	 * @param mc
	 *            the mc to set
	 */
	public void setMc(MathContext mc) {
		this.mc = mc;
	}

	/**
	 * 去除空白字符和在负号'-'前加'0',便于后面的StringTokenizer
	 *
	 * @param exp
	 * @return
	 */
	private static String initExpress(String exp) {
		String reStr = null;
		reStr = exp.replaceAll("\\s", "");
		if (reStr.startsWith("-")) {
			reStr = "0" + reStr;
		}
		reStr = reStr.replaceAll("\\(\\-", "(0-");
		return reStr;
	}

	/**
	 * 是否是整数或是浮点数,但默认-05.15这种也认为是正确的格式
	 *
	 * @param str
	 * @return
	 */
	private boolean isNumber(String str) {
		Pattern p = Pattern.compile("^(-?\\d+)(\\.\\d+)?$");
		Matcher m = p.matcher(str);
		boolean isNumber = m.matches();
		return isNumber;
	}

	/**
	 * 设置优先级顺序()设置与否无所谓
	 *
	 * @param sign
	 * @return
	 */
	private int precedence(String str) {
		char sign = str.charAt(0);
		switch (sign) {
			case '+':
			case '-':
				return 1;
			case '*':
			case '/':
				return 2;
			case '^':
			case '%':
				return 3;
			case '(':
			case ')':
				// case '#':
			default:
				return 0;

		}
	}

	/**
	 * 转变为逆波兰表达式
	 *
	 * @param strList
	 * @return
	 */
	public List<String> initRPN(List<String> strList) {
		List<String> returnList = new ArrayList<String>();
		// 用来存放操作符的栈
		Stack stack = new Stack();
		// stack.push(LOWESTSING);
		int length = strList.size();
		for (int i = 0; i < length; i++) {
			String str = strList.get(i);
			if (isNumber(str)) {
				returnList.add(str);
			} else {
				if (str.equals(OPSTART)) {
					// '('直接入栈
					stack.push(str);
				} else if (str.equals(OPEND)) {
					// ')'
					// 进行出栈操作，直到栈为空或者遇到第一个左括号
					while (!stack.isEmpty()) {
						// 将栈顶字符串做出栈操作
						String tempC = stack.pop();
						if (!tempC.equals(OPSTART)) {
							// 如果不是左括号，则将字符串直接放到逆波兰链表的最后
							returnList.add(tempC);
						} else {
							// 如果是左括号，退出循环操作
							break;
						}
					}
				} else {
					if (stack.isEmpty()) {
						// 如果栈内为空
						// 将当前字符串直接压栈
						stack.push(str);
					} else {
						// 栈不空,比较运算符优先级顺序
						if (precedence(stack.top()) >= precedence(str)) {
							// 如果栈顶元素优先级大于当前元素优先级则
							while (!stack.isEmpty()
									&& precedence(stack.top()) >= precedence(str)) {
								returnList.add(stack.pop());
							}
						}
						stack.push(str);
					}
				}
			}
		}
		// 如果栈不为空，则将栈中所有元素出栈放到逆波兰链表的最后
		while (!stack.isEmpty()) {
			returnList.add(stack.pop());
		}
		return returnList;
	}

	/**
	 * 计算逆波兰表达式
	 *
	 * @param rpnList
	 * @return
	 */
	public String caculate(List<String> rpnList) {
		Stack numberStack = new Stack();

		int length = rpnList.size();
		for (int i = 0; i < length; i++) {
			String temp = rpnList.get(i);
			if (isNumber(temp)) {
				numberStack.push(temp);
			} else {
				BigDecimal tempNumber1 = new BigDecimal(numberStack.pop()); // ,
				// this.mc

				BigDecimal tempNumber2 = new BigDecimal(numberStack.pop()); // ,
				// this.mc

				BigDecimal tempNumber = new BigDecimal("0"); // , this.mc

				if (temp.equals(OP1)) {
					tempNumber = tempNumber2.add(tempNumber1);
				} else if (temp.equals(OP2)) {
					tempNumber = tempNumber2.subtract(tempNumber1);
				} else if (temp.equals(OP3)) {
					tempNumber = tempNumber2.multiply(tempNumber1);
				} else if (temp.equals(OP4)) {
					tempNumber = tempNumber2.divide(tempNumber1, precision,
							roundingMode);
				}

				numberStack.push(tempNumber.toPlainString());

			}
		}

		return numberStack.pop();

	}

	/**
	 * 按照类的缺省参数进行计算
	 *
	 * @return
	 */
	public String caculate() {
		return caculate(this.rpnList);
	}

	/**
	 * 数字条件表达式精确比较 eg: "3.0>2" "1<5" "1==5" "1!=5" "(1.0+2)>3"
	 * "((-0.9+3)>=2. 1)" 不支持&&,||等连接符
	 *
	 * @param strParm
	 * @return
	 */
	public static boolean compareTo(String strParm) {
		boolean reBoolean = false;
		// boolean isParentheses = false;// 标记是否有()括上整个字符串
		String str = initExpress(strParm);
		// Pattern p = Pattern.compile("^\\([\\s\\S]*\\)$");
		// Matcher m = p.matcher(str);
		// isParentheses = m.matches();
		if (-1 == str.indexOf(">=") && -1 == str.indexOf("<=")&& -1 == str.indexOf("=")
				&& -1 == str.indexOf("==") && -1 == str.indexOf("!=")) {
			if (-1 == str.indexOf(">") && -1 == str.indexOf("<"))
				throw new IllegalArgumentException("异常:条件表达式不正确!");
		}
		if (-1 != str.indexOf(">=")) {
			String[] strTemps = str.split(">=");
			strTemps[0] = matchBracket(strTemps[0]);
			strTemps[1] = matchBracket(strTemps[1]);
			int r = new BigDecimal((new MathExpress(strTemps[0]).caculate()))
					.compareTo(new BigDecimal((new MathExpress(strTemps[1])
							.caculate())));
			if (-1 == r) {
				reBoolean = false;
			} else {
				reBoolean = true;
			}
		} else if (-1 != str.indexOf("<=")) {
			String[] strTemps = str.split("<=");
			strTemps[0] = matchBracket(strTemps[0]);
			strTemps[1] = matchBracket(strTemps[1]);
			int r = new BigDecimal((new MathExpress(strTemps[0]).caculate()))
					.compareTo(new BigDecimal((new MathExpress(strTemps[1])
							.caculate())));
			if (1 == r) {
				reBoolean = false;
			} else {
				reBoolean = true;
			}
		} else if (-1 != str.indexOf("==")) {
			String[] strTemps = str.split("==");
			strTemps[0] = matchBracket(strTemps[0]);
			strTemps[1] = matchBracket(strTemps[1]);
			int r = new BigDecimal((new MathExpress(strTemps[0]).caculate()))
					.compareTo(new BigDecimal((new MathExpress(strTemps[1])
							.caculate())));
			if (0 == r) {
				reBoolean = true;
			} else {
				reBoolean = false;
			}
		} else if (-1 != str.indexOf("!=")) {
			String[] strTemps = str.split("!=");
			strTemps[0] = matchBracket(strTemps[0]);
			strTemps[1] = matchBracket(strTemps[1]);
			int r = new BigDecimal((new MathExpress(strTemps[0]).caculate()))
					.compareTo(new BigDecimal((new MathExpress(strTemps[1])
							.caculate())));
			if (0 != r) {
				reBoolean = true;
			} else {
				reBoolean = false;
			}
		} else if ((-1 != str.indexOf(">")) && (-1 == str.indexOf("="))) {
			String[] strTemps = str.split(">");
			strTemps[0] = matchBracket(strTemps[0]);
			strTemps[1] = matchBracket(strTemps[1]);
			int r = new BigDecimal((new MathExpress(strTemps[0]).caculate()))
					.compareTo(new BigDecimal((new MathExpress(strTemps[1])
							.caculate())));
			if (1 == r) {
				reBoolean = true;
			} else {
				reBoolean = false;
			}
		} else if ((-1 != str.indexOf("<")) && (-1 == str.indexOf("="))) {
			String[] strTemps = str.split("<");
			strTemps[0] = matchBracket(strTemps[0]);
			strTemps[1] = matchBracket(strTemps[1]);
			int r = new BigDecimal((new MathExpress(strTemps[0]).caculate()))
					.compareTo(new BigDecimal((new MathExpress(strTemps[1])
							.caculate())));
			if (-1 == r) {
				reBoolean = true;
			} else {
				reBoolean = false;
			}
		} else if (-1 != str.indexOf("=")) {
			String[] strTemps = str.split("=");
			strTemps[0] = matchBracket(strTemps[0]);
			strTemps[1] = matchBracket(strTemps[1]);
			int r = new BigDecimal((new MathExpress(strTemps[0]).caculate()))
					.compareTo(new BigDecimal((new MathExpress(strTemps[1])
							.caculate())));
			if (0 == r) {
				reBoolean = true;
			} else {
				reBoolean = false;
			}
		}
		return reBoolean;
	}

	private static String parseExpression(String expBase) {

		if (expBase.indexOf("IF") != (-1)) {

			expBase = parseIFState(expBase);
		}

		if (expBase.indexOf("CASE") != (-1)) {
			expBase = parseCaseState(expBase);
		}

		return expBase;
	}



//	private static  String parseMaxMinState(String expBase) {
//
//		String str="23+max(11,123,32)-43";
//
//		int matStart=str.indexOf("map");
//
//
//		int i,min,max;
//		int A[]={74,48,30,17,62};  // 声明整数数组A,并赋初值
//
//		min=max=A[0];
//		System.out.print("数组A的元素包括：");
//		for(i=0;i<A.length;i++)
//		{
//		System.out.print(A[i]+" ");
//		if(A[i]>max)   // 判断最大值
//		max=A[i];
//		if(A[i]<min)   // 判断最小值
//		min=A[i];
//		}
//		System.out.println("\n数组的最大值是："+max); // 输出最大值
//		System.out.println("数组的最小值是："+min); // 输出最小值
//		}
//		return expBase;
//	}
//

	private static String parseCaseState(String expBase) {
		int len = 0;
		String charVal = "";

		do {
			int pos = 0;
			int innerCase = 0;
			int posThen = 0;
			int posWhen = 0;

			String expLeft = "";
			String expRight = "";
			String caseVal = "";
			LinkedList<String> exp = new LinkedList<String>();
			LinkedList<String> val = new LinkedList<String>();
			pos = expBase.indexOf("CASE");
			if (pos == (-1)) {
				break;
			} else {
				expLeft = expBase.substring(0, pos);
				expBase = expBase.substring(pos + 4);
				len = expBase.length();
				if (len == 0) {
					break;
				}
			}

			for (int i = 0; i < len; i++) {
				charVal = expBase.charAt(i) + "";
				if (charVal.equals("C")) { // 嵌套出现case when
					innerCase = getInnerCaseEnd(expBase.substring(i)); // 找到内嵌case结束
					i += innerCase + 3;
				} else if (charVal.equals("W")) { // 出现when
					posWhen = i;
					if (posThen > 0) {
						val.add(expBase.substring(posThen + 4, posWhen));
					}
					i += 4;
				} else if (charVal.equals("T")) { // Then
					posThen = i;
					if (posWhen > 0) {
						exp.add(expBase.substring(posWhen + 4, posThen));
					}
					i += 4;
				} else if (charVal.equals("E")) { // Else or End
					// 找到其后的第一个end
					pos = expBase.indexOf("END", i);
					if (pos == -1) { // 表达式出错，没有以end结尾
						break;
					}

					innerCase = expBase.indexOf("CASE", i);
					if (innerCase != (-1)) {
						if (pos > innerCase) { // else end 之间嵌套有case
							innerCase = getInnerCaseEnd(expBase
									.substring(i + 4)); // 找到内嵌case结束
							pos = expBase.indexOf("END", i + 4 + innerCase + 3); // 内嵌case结束后的第一个end
						}
					}
					val.add(expBase.substring(posThen + 4, i)); // 上一个then后面的值
					if (pos != i) { // 含else
						exp.add(expBase.substring(i + 4, pos)+"=="+expBase.substring(i + 4, pos));
						//	exp.add("");
						val.add(expBase.substring(i + 4, pos));
					}
					expRight = expBase.substring(pos + 3);
					break; // 一个case表达式结束
				}
			}

			for (int i = 0; i < (exp.size()); i++) {
				System.out.println("Exp: " + exp.get(i) + "\tVal: "
						+ val.get(i));
			}

			for (int i = 0; i < (exp.size()); i++) {
				charVal = exp.get(i);
				if (charVal.indexOf("CASE") == (-1)) {
					charVal = "(" + charVal + ")";
				} else {
					charVal = parseCaseState(charVal);
				}

				if (compareTo(charVal)) {
					caseVal = val.get(i);
					break;
				}
			}
			if (caseVal != null && !"".equals(caseVal)) {
				//caseVal = val.get(val.size() - 1);
			}
			expBase = expLeft + caseVal + expRight;
			len = expBase.length();

		} while (len > 0);

		return expBase;
	}

	private static int getInnerCaseEnd(String exp) {
		int endIdx = 0;
		int endNums = 0;
		int caseNums = 0;
		String charVal = "";
		for (int i = 0; i < exp.length(); i++) {
			charVal = exp.charAt(i) + "";
			if (charVal.equals("C")) { // case
				caseNums++;
				i += 4;
			} else if (charVal.equals("W")) { // WHEN 之前可嵌套case
				if (endNums == caseNums) {
					break;
				}
				i += 4;
			} else if (charVal.equals("T")) { // THEN
				if (endNums == caseNums) {
					break;
				}
				i += 4;
			} else if (charVal.equals("E")) { // ELSE
				if (exp.substring(i, i + 4).equals("ELSE")) {
					if (endNums == caseNums) {
						break;
					}
					i += 4;
				} else {
					endNums++;
					if (endNums > caseNums) {
						break;
					}
					endIdx = i;
					i += 3;
				}
			}
		}
		System.out.println(exp.substring(0, endIdx + 3));
		return endIdx;
	}

	// 修正括号匹配
	private static String matchBracket(String val) {
		int lBracket = 0;
		int rBracket = 0;
		String charVal = "";
		for (int i = 0; i < val.length(); i++) {
			charVal = val.charAt(i) + "";

			if (charVal.equals("(")) {
				lBracket++;
			} else if (charVal.equals(")")) {
				rBracket++;
			}
		}

		if (lBracket != rBracket) {
			if (lBracket > rBracket) {
				for (int i = 0; i < (lBracket - rBracket); i++) {
					val += ")";
				}
			} else {
				for (int i = 0; i < (rBracket - lBracket); i++) {
					val = "(" + val;
				}
			}
		}

		return val;
	}

	private static String parseIFState(String expBase) {

		int len = 0;

		do {
			int ifIdx = 0;
			String expLeft = "";
			String expRight = "";
			String charVal = "";
			String ifVal = "";

			ifIdx = expBase.indexOf("IF");
			if (ifIdx == (-1)) {
				break;
			} else {
				expLeft = expBase.substring(0, ifIdx) + "(";
				System.out.println("expleft: " + expLeft);
				expBase = expBase.substring(ifIdx + 2);
				System.out.println("expBase:" + expBase);
				len = expBase.length();
			}

			if (len == 0) {
				break;
			}

			String expIf = "";
			String expTrue = "";
			String expFalse = "";
			int lBracket = 0;
			int rBracket = 0;
			int commaNums = 0;
			int commaNeed = 2;
			int commaPos1 = 0;
			int commaPos2 = 0;

			for (int i = 0; i < len; i++) {
				charVal = expBase.charAt(i) + "";
				if (charVal.equals(",")) {
					if (commaPos1 == 0) {
						commaPos1 = i;
						expIf = expBase.substring(0, i);
					}
					commaNums++;
					if (commaPos2 == 0) {
						if (commaNums == commaNeed) {
							commaPos2 = i;
							expTrue = expBase.substring(commaPos1 + 1,
									commaPos2);
						}
					}
				} else if (charVal.equals("(")) {
					lBracket++;
				} else if (charVal.equals(")")) {
					rBracket++;
					// 当左括号数等于右括号数时，IF表达式结束
					if (rBracket == lBracket) {
						expRight = ")" + expBase.substring(i + 1);

						expFalse = expBase.substring(commaPos2 + 1, i);
						break;
					}
				} else if (charVal.equals("I")) {
					// 如为I，则嵌套有IF
					i++;
					commaNeed += 2; // 每一个if需要两个逗号
				}
			}

			if (compareTo(expIf + ")")) {
				ifVal = expTrue;
			} else {
				ifVal = expFalse;
			}

			if (ifVal.indexOf("IF") != (-1)) { // 嵌套了IF
				expBase = ifVal;
			} else { // 结束
				expBase = expLeft + ifVal + expRight;
				expLeft = "";
				expRight = "";
			}

			len = expBase.length();

		} while (len > 0);

		return expBase;
	}

	/**
	 * 栈
	 */
	private class Stack {

		LinkedList<String> stackList = new LinkedList<String>();

		public Stack() {

		}

		/**
		 * 入栈
		 *
		 * @param expression
		 */
		public void push(String expression) {
			stackList.addLast(expression);
		}

		/**
		 * 出栈
		 *
		 * @return
		 */
		public String pop() {
			return stackList.removeLast();
		}

		/**
		 * 栈顶元素
		 *
		 * @return
		 */
		public String top() {
			return stackList.getLast();
		}

		/**
		 * 栈是否为空
		 *
		 * @return
		 */
		public boolean isEmpty() {
			return stackList.isEmpty();
		}
	}

	public static void main(String[] args) {
		//String s = "IF((3)>(IF(5>8,5,8)),IF(5>8,IF(5>8,5,8),IF(15>8,15,8)),IF(5>8,5,IF(5>8,5,8)))";
		//String s = "(case when 12>0  then (case when 12>0  then 78 ELSE 32  end)  end)";
		String  s = "(case  when 2+3>1   then 2-1.5-5    end)";
		// String s = "(CASE WHEN (1>1) THEN 3     WHEN 1<1 THEN 1   WHEN 1>=18 THEN 11 ELSE 123 END)";
		//String s="IF(213>8,5,IF(12>8,IF(11>8,23,8),8))";
		// s =
		// "(case when ((case when 1>2 then 1  when (CASE WHEN 1>2 THEN 1 ELSE 2 END) >= (CASE WHEN 1>2 THEN 1 ELSE 2 END) then (case when 1>2 then 1 else 2 end) else (case when 1>2 then 1 else (case when 1>2 then 1 else 2 end) end) end)>(case when 1>2 then 1 else 2 end)) then (CASE WHEN 1>2 THEN 1 ELSE 2 END) when 21>3 then (case when 1>2 then 1 else 2 end) end)";
		// s =
		// "(case when ((case when 1>2 then 1 else 2 end)>(2)+(0)) then 43+(case when 3>4 then 3 else 4 end) + 34 when 5>6 then 65+ (case when 7>8 then 9 else 10 end)+56 else 1211+ (case when 11>12 then 11 else 12 end)+1112 end)";
		// s =
		// "(case when ((case when 1>2 then 1 else 2 end)>(2)+(0)) then (case when 3>4 then 3 else 4 end) when 6>5 then (case when 7>8 then 9 when 14>13 then (case when 1>2 then (case when 3>4 then 3 else 4 end) when 6>5 then (case when 7>8 then 9 else 10 end) else (case when 11>12 then 11 else 12 end) end) else 10 end) else (case when 11>12 then 11 else 12 end) end)";
	//	String s=	 "(case when ((case when 1>2 then 1  when (CASE WHEN 1>2 THEN 1 ELSE 2 END) >= (CASE WHEN 1>2 THEN 1 ELSE 2 END) then (case when 1>2 then 1 else 2 end) else (case when 1>2 then 1 else (case when 1>2 then 1 else 2 end) end) end)>(case when 1>2 then 1 else 2 end)) then (CASE WHEN 1>2 THEN 1 ELSE 2 END) when 21>3 then (case when 1>2 then 1 else 2 end) end)";

		MathExpress me = new MathExpress(s, 6, RoundingMode.HALF_UP);
		//;System.out.print(me);
		System.out.println(me.caculate());
	}
}